package org.streets.workflow.model;

import java.util.ArrayList;
import java.util.List;

import org.streets.workflow.model.net.WFActivity;
import org.streets.workflow.model.net.WFEndNode;
import org.streets.workflow.model.net.WFLoop;
import org.streets.workflow.model.net.WFNode;
import org.streets.workflow.model.net.WFStartNode;
import org.streets.workflow.model.net.WFSyncRouter;
import org.streets.workflow.model.net.WFTransition;


/**
 * 业务流程。<br/>
 * 这是Fire workflow工作流模型的最顶层元素。
 * 
 */
@SuppressWarnings("serial")
public class WFProcess extends AbstractElement {

	/**
	 * 流程数据项，运行时转换为流程变量进行存储。
	 */
	private List<WFDataField> dataFields = new ArrayList<WFDataField>();

	/**
	 * 全局Task
	 */
	private List<WFTask> tasks = new ArrayList<WFTask>();

	/**
	 * 流程环节
	 */
	private List<WFActivity> activities = new ArrayList<WFActivity>();

	/**
	 * 转移
	 */
	private List<WFTransition> transitions = new ArrayList<WFTransition>();

	/**
	 * 循环
	 */
	private List<WFLoop> loops = new ArrayList<WFLoop>();

	/**
	 * 同步器
	 */
	private List<WFSyncRouter> synchronizers = new ArrayList<WFSyncRouter>();

	/**
	 * 开始节点
	 */
	private WFStartNode startNode = null;

	/**
	 * 结束节点
	 */
	private List<WFEndNode> endNodes = new ArrayList<WFEndNode>();

	// 其他属性
	/**
	 * 资源文件（在1.0中暂时未使用）
	 */
	private String resourceFile = null;

	/**
	 * 资源管理器（在1.0中暂时未使用）
	 */
	private String resourceManager = null;

	/**
	 * 本流程全局的FormTask
	 * Instance运行器。如果没有设置，引擎将使用DefaultFormTaskInstanceRunner来运行TaskInstance。
	 */
	protected String formTaskRunnerName = null;

	/**
	 * 本流程全局的ToolTask
	 * Instance运行器。如果没有设置，引擎将使用DefaultToolTaskInstanceRunner来运行TaskInstance。
	 */
	protected String toolTaskRunnerName = null;

	/**
	 * 本流程全局的SubflowTask
	 * Instance运行器。如果没有设置，引擎将使用DefaultSubflowTaskInstanceRunner来运行TaskInstance。
	 */
	protected String subflowTaskRunnerName = null;

	/**
	 * 本流程全局的FormTask Instance 终结评价器，用于告诉引擎该实例是否可以结束。<br/>
	 * 如果没有设置，引擎使用缺省实现DefaultFormTaskInstanceCompletionEvaluator。
	 */
	protected String formTaskCompletionEvaluatorName = null;

	/**
	 * 本流程全局的ToolTask Instance 终结评价器，用于告诉引擎该实例是否可以结束。<br/>
	 * 如果没有设置，引擎使用缺省实现DefaultToolTaskInstanceCompletionEvaluator。
	 */
	protected String toolTaskCompletionEvaluatorName = null;

	/**
	 * 本流程全局的SubflowTask Instance 终结评价器，用于告诉引擎该实例是否可以结束。<br/>
	 * 如果没有设置，引擎使用缺省实现DefaultSubflowTaskInstanceCompletionEvaluator。
	 */
	protected String subflowTaskCompletionEvaluatorName = null;

	/**
	 * 构造函数
	 * 
	 * @param id
	 * @param name
	 * @param pkg
	 */
	public WFProcess(String name) {
		super(null, name);
	}

	/**
	 * 返回所有的流程数据项
	 * 
	 * @return
	 */
	public List<WFDataField> getDataFields() {
		return dataFields;
	}

	/**
	 * 返回所有的环节
	 * 
	 * @return
	 */
	public List<WFActivity> getActivities() {
		return activities;
	}

	/**
	 * 返回所有的循环
	 * 
	 * @return
	 */
	public List<WFLoop> getLoops() {
		return loops;
	}

	/**
	 * 返回所有的转移
	 * 
	 * @return
	 */
	public List<WFTransition> getTransitions() {
		return transitions;
	}
	/**
	 * 返回开始节点
	 * 
	 * @return
	 */
	public WFStartNode getStartNode() {
		return startNode;
	}

	public void setStartNode(WFStartNode startNode) {
		this.startNode = startNode;
	}

	/**
	 * 返回所有的结束节点
	 * 
	 * @return
	 */
	public List<WFEndNode> getEndNodes() {
		return endNodes;
	}

	/**
	 * 返回所有的同步器
	 * 
	 * @return
	 */
	public List<WFSyncRouter> getSynchronizers() {
		return synchronizers;
	}

	/**
	 * 返回所有的全局Task
	 * 
	 * @return
	 */
	public List<WFTask> getTasks() {
		return this.tasks;
	}

	/**
	 * 保留
	 * 
	 * @return
	 */
	public String getResourceFile() {
		return resourceFile;
	}

	/**
	 * 保留
	 * 
	 * @return
	 */
	public void setResourceFile(String resourceFile) {
		this.resourceFile = resourceFile;
	}

	/**
	 * 保留
	 * 
	 * @return
	 */
	public String getResourceManager() {
		return resourceManager;
	}

	/**
	 * 保留
	 * 
	 * @return
	 */
	public void setResourceManager(String resourceMgr) {
		this.resourceManager = resourceMgr;
	}

	/**
	 * 通过ID查找该流程中的任意元素
	 * 
	 * @param id
	 *            元素的Id
	 * @return 流程元素，如：Activity,Task,Synchronizer等等
	 */
	public WFElement findWFElementById(String id) {
		if (this.getId().equals(id)) {
			return this;
		}

		List<WFTask>  tasksList = this.getTasks();
		for (int i = 0; i < tasksList.size(); i++) {
			WFTask task =  tasksList.get(i);
			if (task.getId().equals(id)) {
				return task;
			}
		}

		List<WFActivity> activityList = this.getActivities();
		for (int i = 0; i < activityList.size(); i++) {
			WFActivity activity = activityList.get(i);
			if (activity.getId().equals(id)) {
				return activity;
			}
			List<WFTask> taskList = activity.getTasks();
			for (int j = 0; j < taskList.size(); j++) {
				WFTask task =  taskList.get(j);
				if (task.getId().equals(id)) {
					return task;
				}
			}
		}
		if (this.getStartNode().getId().equals(id)) {
			return this.getStartNode();
		}
		
		List<WFSyncRouter> synchronizerList = this.getSynchronizers();
		for (int i = 0; i < synchronizerList.size(); i++) {
			WFSyncRouter synchronizer = synchronizerList.get(i);
			if (synchronizer.getId().equals(id)) {
				return synchronizer;
			}
		}

		List<WFEndNode> endNodeList = this.getEndNodes();
		for (int i = 0; i < endNodeList.size(); i++) {
			WFEndNode endNode =  endNodeList.get(i);
			if (endNode.getId().equals(id)) {
				return endNode;
			}
		}

		List<WFTransition> transitionList = this.getTransitions();
		for (int i = 0; i < transitionList.size(); i++) {
			WFTransition transition = transitionList.get(i);
			if (transition.getId().equals(id)) {
				return transition;
			}
		}

		List<WFDataField> dataFieldList = this.getDataFields();
		for (int i = 0; i < dataFieldList.size(); i++) {
			WFDataField dataField =  dataFieldList.get(i);
			if (dataField.getId().equals(id)) {
				return dataField;
			}
		}

		List<WFLoop> loopList = this.getLoops();
		for (int i = 0; i < loopList.size(); i++) {
			WFLoop loop = loopList.get(i);
			if (loop.getId().equals(id)) {
				return loop;
			}
		}
		return null;
	}

//	/**
//	 * 通过Id查找任意元素的序列号
//	 * 
//	 * @param id
//	 *            流程元素的id
//	 * @return 流程元素的序列号
//	 */
//	public String findSnById(String id) {
//		WFElement elem = this.findWFElementById(id);
//		if (elem != null) {
//			return elem.getSn();
//		}
//		return null;
//	}

	/**
	 * 验证workflow process是否完整正确。
	 * 
	 * @return null表示流程正确；否则表示流程错误，返回值是错误原因
	 */
	public String validate() {
		String errHead = "Workflow process is invalid：";
		if (this.getStartNode() == null) {
			return errHead + "must have one start node";
		}
		if (this.getStartNode().getLeavingTransitions().size() == 0) {
			return errHead + "start node must have leaving transitions.";
		}

		List<WFActivity> activities = this.getActivities();
		for (int i = 0; i < activities.size(); i++) {
			WFActivity activity = activities.get(i);
			String theName = (activity.getDisplayName() == null || activity
					.getDisplayName().equals("")) ? activity.getName()
					: activity.getDisplayName();
			if (activity.getEnteringTransition() == null) {
				return errHead + "activity[" + theName
						+ "] must have entering transition.";
			}
			if (activity.getLeavingTransition() == null) {
				return errHead + "activity[" + theName
						+ "] must have leaving transition.";
			}

			// check tasks
			List<WFTask> taskList = activity.getTasks();
			for (int j = 0; j < taskList.size(); j++) {
				WFTask task =  taskList.get(j);
				if (task.getType() == null) {
					return errHead + "task[" + task.getId()
							+ "]'s taskType can Not be null.";
				} else if (task.getType().equals(WFTask.FORM)) {
					WFFormTask formTask = (WFFormTask) task;
					if (formTask.getPerformer() == null) {
						return errHead + "FORM-task[id=" + task.getId()
								+ "] must has a performer.";
					}
				} else if (task.getType().equals(WFTask.AUTO)) {
					WFAutoTask toolTask = (WFAutoTask) task;
					if (toolTask.getApplication() == null) {
						return errHead + "TOOL-task[id=" + task.getId()
								+ "] must has a application.";
					}
				} else if (task.getType().equals(WFTask.SUBFLOW)) {
					WFSubflowTask subflowTask = (WFSubflowTask) task;
					if (subflowTask.getSubProcessModel() == null) {
						return errHead + "SUBFLOW-task[id=" + task.getId()
								+ "] must has a subflow.";
					}
				} else {
					return errHead + " unknown task type of task["
							+ task.getId() + "]";
				}
			}
		}

		List<WFSyncRouter> synchronizers = this.getSynchronizers();
		for (int i = 0; i < synchronizers.size(); i++) {
			WFSyncRouter synchronizer = synchronizers.get(i);
			String theName = (synchronizer.getDisplayName() == null || synchronizer
					.getDisplayName().equals("")) ? synchronizer.getName()
					: synchronizer.getDisplayName();
			if (synchronizer.getEnteringTransitions().size() == 0) {
				return errHead + "synchronizer[" + theName
						+ "] must have entering transition.";
			}
			if (synchronizer.getLeavingTransitions().size() == 0) {
				return errHead + "synchronizer[" + theName
						+ "] must have leaving transition.";
			}
		}

		List<WFEndNode> endnodes = this.getEndNodes();
		for (int i = 0; i < endnodes.size(); i++) {
			WFEndNode endnode = endnodes.get(i);
			String theName = (endnode.getDisplayName() == null || endnode
					.getDisplayName().equals("")) ? endnode.getName() : endnode
					.getDisplayName();
			if (endnode.getEnteringTransitions().size() == 0) {
				return errHead + "end node[" + theName
						+ "] must have entering transition.";
			}
		}

		List<WFTransition> transitions = this.getTransitions();
		for (int i = 0; i < transitions.size(); i++) {
			WFTransition transition = transitions.get(i);
			String theName = (transition.getDisplayName() == null || transition
					.getDisplayName().equals("")) ? transition.getName()
					: transition.getDisplayName();
			if (transition.getFromNode() == null) {
				return errHead + "transition[" + theName
						+ "] must have from node.";

			}
			if (transition.getToNode() == null) {
				return errHead + "transition[" + theName
						+ "] must have to node.";
			}
		}

		// check datafield
		List<WFDataField> dataFieldList = this.getDataFields();
		for (int i = 0; i < dataFieldList.size(); i++) {
			WFDataField df =  dataFieldList.get(i);
			if (df.getDataType() == null) {
				return errHead + "unknown data type of datafield[" + df.getId()
						+ "]";
			}
		}

		return null;
	}

	/**
	 * 判断是否可以从from节点到达to节点
	 * 
	 * @param fromNodeId
	 *            from节点的id
	 * @param toNodeId
	 *            to节点的id
	 * @return
	 */
	public boolean isReachable(String fromNodeId, String toNodeId) {
		if (fromNodeId == null || toNodeId == null) {
			return false;
		}
		if (fromNodeId.equals(toNodeId)) {
			return true;
		}
		List<WFNode> reachableList = this.getReachableNodes(fromNodeId);

		for (int j = 0; reachableList != null && j < reachableList.size(); j++) {
			WFNode node =  reachableList.get(j);
			if (node.getId().equals(toNodeId)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * 判断两个Activity是否在同一个执行线上
	 * 
	 * @param activityId1
	 * @param activityId2
	 * @return true表示在同一个执行线上，false表示不在同一个执行线上
	 */
	public boolean isInSameLine(String activityId1, String activityId2) {
		WFNode node1 = (WFNode) this.findWFElementById(activityId1);
		WFNode node2 = (WFNode) this.findWFElementById(activityId2);
		if (node1 == null || node2 == null)
			return false;
		List<WFNode> connectableNodes4Activity1 = new ArrayList<WFNode>();
		connectableNodes4Activity1.add(node1);
		connectableNodes4Activity1.addAll(getReachableNodes(activityId1));
		connectableNodes4Activity1.addAll(getEnterableNodes(activityId1));

		List<WFNode> connectableNodes4Activity2 = new ArrayList<WFNode>();
		connectableNodes4Activity2.add(node2);
		connectableNodes4Activity2.addAll(getReachableNodes(activityId2));
		connectableNodes4Activity2.addAll(getEnterableNodes(activityId2));
		/*
		 * System.out.println("===Inside WorkflowProcess.isInSameLine()::connectableNodes4Activity1.size()="
		 * +connectableNodes4Activity1.size());System.out.println(
		 * "===Inside WorkflowProcess.isInSameLine()::connectableNodes4Activity2.size()="
		 * +connectableNodes4Activity2.size());
		 * System.out.println("-----------------------activity1--------------");
		 * for (int i=0;i<connectableNodes4Activity1.size();i++){ Node node =
		 * (Node)connectableNodes4Activity1.get(i);
		 * System.out.println("node.id of act1 is "+node.getId()); }
		 * 
		 * 
		 * System.out.println("---------------------activity2--------------------"
		 * ); for (int i=0;i<connectableNodes4Activity2.size();i++){ Node node =
		 * (Node)connectableNodes4Activity2.get(i);
		 * System.out.println("node.id of act2 is "+node.getId()); }
		 */

		if (connectableNodes4Activity1.size() != connectableNodes4Activity2.size()) {
			return false;
		}

		for (int i = 0; i < connectableNodes4Activity1.size(); i++) {
			WFNode node =  connectableNodes4Activity1.get(i);
			boolean find = false;
			for (int j = 0; j < connectableNodes4Activity2.size(); j++) {
				WFNode tmpNode = connectableNodes4Activity2.get(j);
				if (node.getId().equals(tmpNode.getId())) {
					find = true;
					break;
				}
			}
			if (!find)
				return false;
		}
		return true;
	}
    /**
     * 获取可以到达的节点
     * @param nodeId
     * @return
     */
	public List<WFNode> getReachableNodes(String nodeId) {
		List<WFNode> reachableNodesList = new ArrayList<WFNode>();
		WFNode node = (WFNode) this.findWFElementById(nodeId);
		if (node instanceof WFActivity) {
			WFActivity activity = (WFActivity) node;
			WFTransition leavingTransition = activity.getLeavingTransition();
			if (leavingTransition != null) {
				WFNode toNode = leavingTransition.getToNode();
				if (toNode != null) {
					reachableNodesList.add(toNode);
					reachableNodesList.addAll(getReachableNodes(toNode.getId()));
				}
			}
		} else if (node instanceof WFSyncRouter) {
			WFSyncRouter synchronizer = (WFSyncRouter) node;
			List<WFTransition> leavingTransitions = synchronizer.getLeavingTransitions();
			for (int i = 0; leavingTransitions != null
					&& i < leavingTransitions.size(); i++) {
				WFTransition leavingTransition =  leavingTransitions.get(i);
				if (leavingTransition != null) {
					WFNode toNode = (WFNode) leavingTransition.getToNode();
					if (toNode != null) {
						reachableNodesList.add(toNode);
						reachableNodesList.addAll(getReachableNodes(toNode.getId()));
					}

				}
			}
		}
        //剔除重复节点
        List<WFNode> tmp = new ArrayList<WFNode>();
		boolean alreadyInTheList = false;
		for (int i = 0; i < reachableNodesList.size(); i++) {
			WFNode nodeTmp = reachableNodesList.get(i);
			alreadyInTheList = false;
			for (int j = 0; j < tmp.size(); j++) {
				WFNode nodeTmp2 = tmp.get(j);
				if (nodeTmp2.getId().equals(nodeTmp.getId())) {
					alreadyInTheList = true;
					break;
				}
			}
			if (!alreadyInTheList) {
				tmp.add(nodeTmp);
			}
		}
		reachableNodesList = tmp;
		return reachableNodesList;
	}
    /**
     * 获取进入的节点(activity 或者synchronizer)
     * @param nodeId
     * @return
     */
	public List<WFNode> getEnterableNodes(String nodeId) {
		List<WFNode> enterableNodesList = new ArrayList<WFNode>();
		WFNode node = (WFNode) this.findWFElementById(nodeId);
		if (node instanceof WFActivity) {
			WFActivity activity = (WFActivity) node;
			WFTransition enteringTransition = activity.getEnteringTransition();
			if (enteringTransition != null) {
				WFNode fromNode = enteringTransition.getFromNode();
				if (fromNode != null) {
					enterableNodesList.add(fromNode);
					enterableNodesList.addAll(getEnterableNodes(fromNode.getId()));
				}
			}
		} else if (node instanceof WFSyncRouter) {
			WFSyncRouter synchronizer = (WFSyncRouter) node;
			List<WFTransition> enteringTransitions = synchronizer.getEnteringTransitions();
			for (int i = 0; enteringTransitions != null
					&& i < enteringTransitions.size(); i++) {
				WFTransition enteringTransition =  enteringTransitions.get(i);
				if (enteringTransition != null) {
					WFNode fromNode = enteringTransition.getFromNode();
					if (fromNode != null) {
						enterableNodesList.add(fromNode);
						enterableNodesList.addAll(getEnterableNodes(fromNode.getId()));
					}

				}
			}
		}

	    //剔除重复节点 
		//TODO mingjie.mj 20091018 改为使用集合是否更好?
        List<WFNode> tmp = new ArrayList<WFNode>();
		boolean alreadyInTheList = false;
		for (int i = 0; i < enterableNodesList.size(); i++) {
			WFNode nodeTmp = enterableNodesList.get(i);
			alreadyInTheList = false;
			for (int j = 0; j < tmp.size(); j++) {
				WFNode nodeTmp2 =  tmp.get(j);
				if (nodeTmp2.getId().equals(nodeTmp.getId())) {
					alreadyInTheList = true;
					break;
				}
			}
			if (!alreadyInTheList) {
				tmp.add(nodeTmp);
			}
		}
		enterableNodesList = tmp;
		return enterableNodesList;
	}

	public String getFormTaskCompletionEvaluatorName() {
		return formTaskCompletionEvaluatorName;
	}

	public void setFormTaskCompletionEvaluatorName(
			String formTaskInstanceCompletionEvaluator) {
		this.formTaskCompletionEvaluatorName = formTaskInstanceCompletionEvaluator;
	}

	public String getFormTaskRunnerName() {
		return formTaskRunnerName;
	}

	public void setFormTaskRunnerName(String formTaskInstanceRunner) {
		this.formTaskRunnerName = formTaskInstanceRunner;
	}

	public String getSubflowTaskCompletionEvaluatorName() {
		return subflowTaskCompletionEvaluatorName;
	}

	public void setSubflowTaskCompletionEvaluatorName(
			String subflowTaskInstanceCompletionEvaluator) {
		this.subflowTaskCompletionEvaluatorName = subflowTaskInstanceCompletionEvaluator;
	}

	public String getSubflowTaskRunnerName() {
		return subflowTaskRunnerName;
	}

	public void setSubflowTaskRunnerName(String subflowTaskInstanceRunner) {
		this.subflowTaskRunnerName = subflowTaskInstanceRunner;
	}

	public String getToolTaskRunnerName() {
		return toolTaskRunnerName;
	}

	public void setToolTaskRunnerName(String toolTaskInstanceRunner) {
		this.toolTaskRunnerName = toolTaskInstanceRunner;
	}

	public String getToolTaskCompletionEvaluatorName() {
		return toolTaskCompletionEvaluatorName;
	}

	public void setToolTaskCompletionEvaluatorName(
			String toolTaskIntanceCompletionEvaluator) {
		this.toolTaskCompletionEvaluatorName = toolTaskIntanceCompletionEvaluator;
	}
}
